package com.cjm.nio.ifeve;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;

/**
 * Buffer的本质是一块内存， 提供了一列表的方法， 用来方便的访问该块内存
 * 1. 基本用法
 *  四个步骤
 *      1. 写入数据到Buffer中
 *      2. 调用flip()方法, 将写模式切换成读模式
 *      3. 从Buffer中读取数据
 *      4. 调用clear()方法或者compact()方法
 *      当向Buffer中写入数据时， Buffer会记录下写了多少数据， 一旦要读取数据， 需要通过flip()方法Buffer从写模式切换到
 *      读取模式， 在读模式下， 可以读取之前写入到Buffer中的所有数据
 * 2. Capacity， Position， Limit
 * 3. 类型
 * 4. 分配
 * 5. 写数据
 * 6. flip()
 * 7. 读取数据
 * 8. clear() compact()
 * 9. mark() reset()
 * 10. equals() compareTo()
 */
public class BufferDemo {

    public static void main(String[] args) throws Exception {
        //m1();
//        m2();
        markAndReset();
    }

    /**
     * 可以标记Buffer中的一个特定position， 之后可以通过调用Buffer.reset()方法恢复到这个position
     */
    private static void markAndReset() {
        ByteBuffer buf = ByteBuffer.allocate(16);
        buf.put("a".getBytes());
        buf.put("b".getBytes());
        buf.put("c".getBytes());
        buf.put("d".getBytes());
        buf.put("e".getBytes());
        buf.put("f".getBytes());
        buf.put("g".getBytes());
        buf.put("j".getBytes());
        buf.put("h".getBytes());

        buf.flip();

        System.err.println((char)buf.get());
        System.err.println((char)buf.get());
        buf.mark();
        System.err.println((char)buf.get());
        System.err.println((char)buf.get());
        buf.reset();
        System.err.println((char)buf.get());
        System.err.println((char)buf.get());

        buf.clear();
        buf.position(6);
        buf.flip();
        System.err.println((char)buf.get());
    }

    /**
     * 一旦读完Buffer中的数据， 需要让Buffer准备好再次被写入， 可以通过clear()或compact()方法来完成
     * clear() -> position = 0, limit = capacity, Buffer被清空， Buffer中的数据并未清除， 只
     * 是这些标记告诉我们可以从哪里开始往Buffer中写数据
     *
     * 如果Buffer中还有一些未读数据， 调用clear()方法， 数据将被”遗忘“， 意味着不再有任何标记会告诉你
     * 哪些数据被读过， 哪些还没有
     *
     * 如果Buffer中仍有未读数据， 且后续还需要这些数据， 但是此时想要先写些数据， 那么使用compact()方法
     *
     * compact()方法将所有未读的数据拷贝到Buffer起始处， 然后将position设到最后一个未读元素正后面， limit
     * 属性依然像clear()方法一样， 设置成capacity， 现在Buffer准备好写数据了， 但是不会覆盖未读的数据
     */
    private static void clearAndCompact() {

    }

    /**
     * 将position设回0， 所以你可以重读Buffer中的所有数据， limit数据保持不变， 仍然表示能从Buffer中
     * 读取多少个元素
     */
    private static void rewind(){

    }

    /**
     * flip()将Buffer从写模式切换到读模式， 调用flip()方法会将position设回0， 并将limit设置成之前的position的值
     * 换句话说， position现在用于标记读位置， limit表示之前写进了多少个byte, char等--现在能读取多少个byte, char等
     */
    private static void flip() {

    }

    /**
     * 三个重要的属性
     * capacity: 容量
     * position: 位置
     *  当你写数据到Buffer中时， position表示当前的位置， 初始的position值为0， 当一个byte, long数据写入到Buffer中后
     *  position会向前移动到下一个可插入数据的Buffer单元， position最大可为capacity = -1
     *
     *  当读取数据时， 也是从某个特定位置读， 当将Buffer从写模式切换到读模式， position会被重置为0， 当从Buffer的
     *  position处读取数据时， position向前移动到下一个可读的位置
     * limit: 限制
     *  在写模式下， Buffer的limit表示你最多能往Buffer里写多少数据， limit = capacity
     *  切换到读模式时， limit表示你最多能读到多少数据。因此， 当切换Buffer到读模式时， limit会被设置成写模式下的position
     *  值， 换句话说， 你能读到之前写入的所有数据（limit被设置成已写数据的数量， 这个值在写械下就是position）
     *
     * @throws Exception
     */
    private static void m2() throws Exception {

    }

    private static void m1() throws IOException {
        RandomAccessFile accessFile = new RandomAccessFile("D:\\a.txt", "rw");
        FileChannel channel = accessFile.getChannel();
        // offset = 0, isReadOnly = false, bigEndian = true, nativeByteOrder = false
        // mark = -1, position = 3, limit = 48, capacity = 48, address = 16
        ByteBuffer buf = ByteBuffer.allocate(48);
        int bytesRead = channel.read(buf);
        while (bytesRead != -1) {
            // offset = 0, isReadOnly = false, bigEndian = true, nativeByteOrder = false
            // mark = -1, position = 0, limit = 3, capacity = 48, address = 16
            buf.flip();
            // offset = 0, isReadOnly = false, bigEndian = true, nativeByteOrder = false
            // mark = -1, position = 0, limit = 3, capacity = 48, address = 16
            while (buf.hasRemaining()) {
                System.err.println((char)buf.get());
            }
            buf.clear();
            bytesRead = channel.read(buf);
        }
        accessFile.close();
    }
}
